home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000
/
Ham Radio 2000.iso
/
ham2000
/
packet
/
p_tapr
/
kissv7
/
kiss_v7.mac
< prev
next >
Wrap
Text File
|
1991-06-27
|
49KB
|
1,887 lines
;
; KISS TNC for the TNC-2 and clones
;
; k3mc 30 Sep 86 - original version
;
; 1 Mar 87. Fixed all known bugs. Re-arrange code to allow ROMing (this
; means that data areas need to be initialized from the code). Figure out the
; Stack Pointer given the amount of available RAM. Include the codes 05 00
; and 05 01 to mean full duplex off and full duplex on, respectively.
; Clear out all available RAM. Do a "dance" with LEDs when initially booted:
; Flash the LED(s) for about 5 seconds such that CON only flashes if you have
; 8k RAM, STA only flashes if 16k RAM, and STA and CON flash if 32k RAM.
;
; 29 Mar 87. Add code to discard BREAK chars, and chars with framing errors.
; Fix bug in ib_rca which did not discard null received frames.
;
; 11 Dec 89. Incorporate code from Jan Schiefer, DL5UE, [44.130.48.9]
; Degerlocherstrasse 5, 7000 Stuttgart 70, Federal republic of Germany
; to fix the problem with Full-Duplex operation. New version number, v.4
;
; 19 Jan 91. Shin-ichi Kanno , JN1JDZ , [133.168.32.129]
; Fix the DCD detection problem. Add the auto-enable operation.
; Add the code of output following data routine. New version number, v.5
;
; 27 Jun 91. Add the TXdelay control . Fix buffer allocation problem .
; Add the code of Software DCD Detection routine. New version number, v.7
;
FALSE equ 0
TRUE equ NOT FALSE
ALONE equ TRUE ;uncomment this line to get stand-alone code.
;TASCO equ TRUE ;uncomment this line to get for TASCO's TNC's
;(TNC-20,TNC-20H and TNC-22) ROM code.
SIO_AUTO equ TRUE ;uncomment this line to set SIO PORT B for
;auto-enable mode.
HARDWARE equ TRUE ;uncomment this line to add the set hardware
;sub command.
; .z80
; aseg
; org 100h ;silly stuff for CP/M...
ifdef ALONE
.phase 0000h
Free_RAM equ 8000h
else
ifdef TASCO
.phase 7000h
Free_RAM equ 0d000h
else
.phase 7800h
Free_RAM equ 9000h
endif
endif
SIO equ 0dch ;actually, only A5 is used for SIO -cs
A_dat equ SIO+0 ;Modem port
A_ctl equ SIO+1 ;Modem port
B_dat equ SIO+2 ;user serial port
B_ctl equ SIO+3 ;user serial port
DCD equ 8 ;Bit in RR0, used in Ch A
CTS equ 32 ;Bit in RR0, used in Ch A
TBE equ 4 ;TX Buffer Empty bit
RTS equ 2 ;Request To Send (PTT bit in WR5 of Chan A)
Framing_Error equ 40h ;Bit in RR1 for async framing error
Break_Abort equ 80h ;Bit in RR0 for async Break detection
ifdef SIO_AUTO
Auto_Enable equ 0e1h
else
Auto_Enable equ 0c1h
endif
FEND equ 300o ;300 octal
FESC equ 333o ;333 octal
TFEND equ 334o ;334 octal
TFESC equ 335o ;335 octal
ALEDon equ 69h ;bits for WR5 to turn on STA LED
ALEDoff equ 0e9h ;bits for WR5 to turn off STA LED
ALED equ 80h ;The DTR Bit in Ch A WR5, we will soon remove
;previous 2 definitions & use the memory loc.
;A_WR5 to hold Ch A WR5's value, because we
;need to be aware when we are transmitting!
BLEDon equ 6ah ;bits for WR5 to turn on CON LED
BLEDoff equ 0eah ;bits for WR5 to turn off CON LED
BLED equ 80h
start:
jp code_start ;go around this data area
version:
db 'v.7 27 Jun 91'
;13 bytes (exactly!) here for version string
I_Vector:
dw ib_tbe ;ch B transmitter buffer empty interrupt/user
dw ib_ext ;ch B ext/status change/user
dw ib_rca ;ch B received char available/user
dw ib_special ;ch B special receive condition/user
dw ia_tbe ;ch A transmitter buffer empty interrupt/modem
dw ia_ext ;ch A ext/status change/modem
dw ia_rca ;ch A received char available/modem
dw ia_special ;ch A special receive condition/modem
code_start:
di ;No interrupts for the moment...
;Init SIO. This is required even if we wanna flash LEDs...
in a,(A_ctl) ;assure we are talking to ch 0
ld c,A_ctl
ld b,a_size
ld hl,a_init
otir ;init sync (modem) port
;Init Async port, also to allow flashing LEDs
in a,(B_ctl) ;assure we are talking to ch 0
ld c,B_ctl
ld b,b_size
ld hl,b_init
otir ;init async port & interrupt vector
;
; Figure out where top of stack is, set stack pointer.
; 32K RAM system.
ld sp,0 ;[JDZ]
;Clear out RAM.
ld bc,0ffffh-Free_RAM-1 ;[JDZ] get Byte Count into BC
ld hl,Free_RAM ;[JDZ]
ld (hl),0 ;[JDZ]
ld de,Free_RAM+1 ;[JDZ] get "source" address = Free_RAM
ldir ;Zero memory.
;This sequence loads up our data area in RAM:
ld hl,data_init
ld de,TXdelay
ld bc,data_size
ldir
; init free buffer list.
ld hl,Bottom ;[JDZ] beginning of buffer space
;[JDZ] now it's also top of free list
ld b,-1+(-100-Bottom)/128 ;[JDZ] get buffers - 1
ibloop:
push hl
ld de,128
add hl,de ;HL has "next" pointer
ex de,hl ;DE has "next" pointer
pop hl ;HL now has pointer to current buffer
ld (hl),e ;low byte of "next" pointer first
inc hl
ld (hl),d ;now hi byte
inc hl
xor a
ld (hl),a ;zero out count field
inc hl
ld (hl),a ;zero out # of bytes read field
ex de,hl ;HL is now pointer to next buffer
djnz ibloop ;and init all the available buffers
xor a
ld (hl),a ;Last "next" address is 0
inc hl
ld (hl),a ;ditto
inc hl
ld (hl),a ;zero out count field
inc hl
ld (hl),a ;zero out # of bytes read field
;init regs for ib_ext interrupt
exx
ld bc,0 ;set prev state of SYNC pin,for 1200hz
ld de,0 ;count of # of interrupts init
exx
;[JDZ] Now have the CON and STA LEDs do a "dance".
ld b,6 ;Do it 6 times (arbitrary as hell, but should
;be an even number so that the LEDs are off at
;the end of this mess...)
ld hl,0 ;use HL as downcounter
dance0:
call CON_Flip
call STA_Flip
dance1:
dec hl
ld a,h
or l
jr nz,dance1
djnz dance0 ;do this 6 times (3 "cycles")
;Previous stuff showed that the download or boot worked properly...
;We re-initialize the SIO ports so that we flush garbage chars that may have
;come in while we were diddling the LEDs. This is necessary because unless we
;do this, then the A channel (modem) get RX overrun (esp if TNC was listening
;to noise) and RX overrun is VERY BAD - so bad, in fact, that I turn on both
;CON and STA and halt, because this situation should NEVER happen in normal
;use. I flush the B (tty) channel in case anything was sent to it in mid-
;stream.
;Re-Init SIO.
in a,(A_ctl) ;assure we are talking to ch 0
ld c,A_ctl
ld b,a_size
ld hl,a_init
otir ;init sync (modem) port
;Re-Init Async port.
in a,(B_ctl) ;assure we are talking to ch 0
ld c,B_ctl
ld b,b_size
ld hl,b_init
otir ;init async port & interrupt vector
; Prepare to load hi bits of interrupt vector
ld a,I_Vector/256
ld i,a ;set interrupt page for mode 2 ints
im 2
ei ;let 'em rip!
;-----------------------------------------------------------------------------
; This is the background program.
; Note that since everything else is interrupt driven, and saves registers,
; this part of the code can use registers & expect values to stay.
Commutator_loop:
call TX_data
call Host_TX_data
jp Commutator_loop
;Now see if we need to start an output to RS-232 (host) port
Host_TX_data:
ld a,(out_started)
or a ;also clears carry (see below)
ret nz ;if output started, nothing to do
in a,(B_ctl) ;look at RR0
and TBE ;isolate the TBE bit
ret z
; else we should check to see if we need to start an output
di
call CON_off ;
ld hl,(out_head_cbuf) ;grab current top of circ buf ptr
ld de,(out_tail_cbuf) ;and where the next free buf ptr is
ei
;interrupt protect the pickup of the
;two pointers 3 Feb 87
or a
sbc hl,de
ret z ;if the same, nothing to do
;else we need to start an output
di ;interrupt protect this section,
;although I'm not sure it needs it...
;3 Feb 87
;note: it should already BE done!
ld hl,(out_head_cbuf) ;get pointer to next cbuf to output
ld e,(hl)
inc hl
ld d,(hl) ;DE has pointer to buffer chain
ld (out_chain_head),de ;set in interrupt routine's place
ld a,TRUE
ld (out_started),a ;yes, output started
call CON_on
ld a,FEND
out (B_dat),a ;send FEND character (start txing)
ei
ret ;keep looking for new opportunity
TX_data:
ld a,(TX_State)
or a
jp z,txd0
cp 1
jp z,txd1
cp 2
jp z,txd2
cp 3
jp z,txd3
cp 4
ret z
;When tail timer times out, turn off the TX
ld a,(TX_Timer)
or a
ret nz
ld a,5 ;ready to write to WR5 of Ch A
di ;must have atomic use of A_WR5 & SIO
out (A_ctl),a ;Next char to A_ctl goes to WR5
ld a,(A_WR5) ;grab A_WR5
and NOT RTS ;turn off RTS bit there
ld (A_WR5),a ;keep memory copy updated
out (A_ctl),a ;and turn off TX now
xor a
ld (TX_State),a
ei
ret
txd0:
ld a,(TX_outstanding) ;if there are no outstanding TX...
or a ;...frames, then we don't have to...
ret z ;...worry about Transmitter
; do persistence algorithm
ld a,r ;grab the Z-80 refresh register
add a,a ;double;now 0 <= A reg <= 254
ld b,a ;B holds our "random" number
ld a,(Persistence)
sub b ;A reg = Persistence - Random #
jp c,No_PTT ;if (P-r) < 0 then no PTT now
; Note that P=255 means ALWAYS key up
; else we've noticed that we've got some frame(s) to send.
; try to keyup TX
ld a,(Full_Duplex)
or a
jp nz,Key_Up ;if Full Duplex, then there is no
;need to worry about all this silly
;slot time and persistence stuff!
ifdef HARDWARE
;check soft DCD.
ld a,(Soft_DCD)
ld l,a
bit 1,l
jp z,txd1a
ld a,(RX_State)
or a
jp nz,No_PTT
;check if Carrier Detect is active
txd1a:
bit 0,l
jp z,Key_Up
endif
ld a,(A_RR0) ;A_RR0 is set in interrupt routine
and DCD
jp nz,No_PTT ;[JDZ] If carrier active, wait it out
;OK, so we've won with the random number generator. Keyup TX and start the
;TXdelay timer
Key_Up:
ld a,(TXdelay)
ld (TX_Timer),a ;Get timer value into timer slot
ld a,5
di ;we need quite time here.
out (A_ctl),a ;Ready to write into WR5 of Ch A
ld a,(A_WR5)
or RTS ;Turn on the PTT bit...
ld (A_WR5),a ;...in the memory copy of WR5
out (A_ctl),a ; Keyup transmitter
ld a,2
ld (TX_State),a
ei
ret ;That's all we do for now, we await
;TXdelay event
No_PTT: ;since we lost on Random #, wait SlotTime before trying again
ld a,(SlotTime)
ld (TX_Timer),a ;Set up the timer value of this event
ld a,1
ld (TX_State),a
ret
txd1:
ld a,(TX_Timer)
or a
ret nz
xor a
ld (TX_State),a
ret
txd2:
ld a,(TX_Timer)
or a
ret nz
ld a,3
ld (TX_State),a
ret
txd3:
ifdef HARDWARE
; CTS flow
ld a,(CTS_Control)
or a
jp z,txd3a
ld a,(A_RR0)
and CTS
ret z
txd3a:
endif
di
ld a,4
ld (TX_State),a
call TXnext_CBuf ;gets HL to point to buffer chain, and
;sets TX_Chain_Head for the interrupt
;routine
ld a,80h
out (A_ctl),a ; reset TX CRC
call getchar ; getchar needs int. protection
out (A_dat),a ; Ship this char to TX modem
ld a,TRUE
ld (TX_Started),a ; and, yes Virgina, we've started TX
ld a,0c0h
out (A_ctl),a ; reset TX underrun/EOM latch
ei
ret
; include IA.MAC ;Modem interrupt catchers
;---------------------------------------------------------------------------
ia_tbe:
push af
push hl
ld a,(TX_Started)
or a
jp z,ia_t2 ; previous frame finished
ld hl,(TX_Chain_Head)
call getchar
ld (TX_Chain_Head),hl ; must keep this pointer updated
jp z,ia_t1 ; no more to send
out (A_dat),a ; else ship this char out
ia_t9:
pop hl
pop af
ei
reti ; just return from these interrupts
ia_t1:
; halt ;if it gets here, halt
xor a
ld (TX_Started),a ; TX is NOT started
ld hl,TX_Outstanding ; make is so that one fewer frames
; NOT "(TX_Outstanding)" (!) 29 Sep
dec (hl) ; are outstanding
ld a,28h
out (A_ctl),a ; reset TX interrupt pending
jp ia_t9
;previous frame is done, SIO now sending a flag. More?
ia_t2:
ld a,(TX_Outstanding)
or a
jp nz,ia_t21 ;if more to send, go there
; else we're done here, clean up.
ld a,28h
out (A_ctl),a ; Reset TX interrupt pending
;start Tail timer event
ld a,(TailTime)
ld (TX_Timer),a ; wait for CRC to clear TX
ld a,5
ld (TX_State),a
jp ia_t9
ia_t21: ;start up next frame
call TXnext_CBuf ; get the next buffer chain pointer
; setup HL and TX_Chain_Head
ld a,80h
out (A_ctl),a ; reset TX CRC generator
call getchar
out (A_dat),a ;get 1st char of next frame
ld a,TRUE
ld (TX_Started),a ; TX started again
ld a,0c0h
out (A_ctl),a ; reset TX underrun/EOM latch
jp ia_t9
;---------------------------------------------------------------------------
; Got a character from the SIO RX interrupt, deal with it
; Extensive mods 3 Feb 87 to be in line with what I now know about SIO...
ia_rca:
push af
push hl
ld a,(RX_flushing)
or a
jp z,ia_rc1
in a,(A_dat)
jp ia_rc9
ia_rc1:
ld a,(RX_Allocated_Buffer)
or a
jp nz,ia_rc7 ; Go there if we are in "receiving" state
;else we are not yet receiving, so allocate buffer & make us "receiving"
call allocate_buffer ; get a new buffer
jp z,ia_rc5 ; NO ROOM, flush this frame
; if got a buffer, insert this character.
; after doing initial buffer setup.
ia_rc6:
ld (RX_head),hl ; save chain head address (1st buffer)
ld (RX_buf),hl ; tuck away addr of our current buffer
ld a,TRUE
ld (RX_Allocated_Buffer),a ; and mark that
; we are receiving
ld a,0 ; Channel 0
call putchar ; SLIP' frame "type" field here (Always 0)
ia_rc7:
in a,(A_dat) ; grab the pending character
ld hl,(RX_buf) ; load up address of our current RX buffer
call putchar ; and stuff in this particular buffer
jp c,ia_rc2 ; If NO ROOM, flush this frame.
ld (RX_buf),hl ; HL might have changed in putchar()
ia_rc9:
pop hl
pop af
ei
reti ; nothing else to do here
; if no room, flush this frame (sigh)
ia_rc2:
xor a
ld (RX_Allocated_Buffer),a
ld hl,(RX_head)
call free_chain
ia_rc5:
ld a,TRUE
ld (RX_flushing),a ; we are in the midst of
; flushing this frame
call STA_on ;ddd Note that we are in flushing
;state
jp ia_rc9
;---------------------------------------------------------------------------
; From out point of view, this interrupt is only interesting because it
; tells us if we're at end of frame.
ia_special:
push af
push hl ; regs we'll need
ld a,1
out (A_ctl),a ; ready to read RR1
in a,(A_ctl) ; OK, grab RR1
; First check if RX overrun. This is VERY BAD, so what can we do?
; Well, we merely treat it as a bad CRC, that is, just flushing the
; frame. I don't like dropping chars (and it shouldn;t happen very often)
; but at high speeds, it may occur with 2.5 MHz z80s.
bit 5,a ; RX overrun?
jp nz,ia_sp8 ; If a problem, treat as bad CRC
; That is, flush this frame....
;ia_sp0:
bit 7,a ; check state of End of Frame bit
jp z,ia_sp8 ; Else something weird happened - probably
; RX overrun. In any case, flush this frame.
; error reset & then exit
; that is, treat like it was a CRC error
; If End of Frame, check CRC bit for valid.
ia_sp1:
bit 6,a ; Check CRC error bit
jp nz,ia_sp8 ; If CRC error bit is on, then was CRC error
; First ensure that we indeed have a buffer allocated...
ld a,(RX_Allocated_Buffer)
or a
jp z,ia_sp9 ; if no buffer allocated, ignore this.
; Else this was a good frame, and we should ship it out to host
; Leave the first CRC character at end of buffer chain in the buffer, as
; getchar() will flush it.
ld hl,(RX_head)
call out_queue_insert ; Shove this buffer string onto
; output queue
xor a
ld (RX_Allocated_Buffer),a ; We don't have a buffer
; allocated for the next
; frame...
jp ia_sp9
; get here if there was a bad CRC
ia_sp8:
ld a,(RX_Allocated_Buffer) ; If we don't have any
; buffers allocated, then
or a ;8 Feb - SET CONDITION CODES !!!!!!
jp z,ia_sp9 ; we MUST NOT "release" them !!! 10 Sep 86
; if they are not allocated !!!
xor a
ld (RX_Allocated_Buffer),a
; not receiving if we have bad CRC
ld hl,(RX_head)
call free_chain ; free up all buffer(s)
ia_sp9:
ld a,30h ; error reset
out (A_ctl),a
in a,(A_dat) ; Avoid spurious RCA interrupt
ld a,03h ; [JS] select WR3
out (A_ctl),a ; [JS]
ld a,0D9h ; [JS] enter hunt mode
out (A_ctl),a ; [JS]
xor a
ld (RX_State),a ; [JS] store sync/hunt state
ld (RX_flushing),a
ifdef HARDWARE
; Software DCD filetr
ld a,(Full_Duplex)
or a
jp nz,ia_spsd
ld a,(TX_State)
cp 2
jp nc,ia_spsd
ld a,(Soft_DCD)
bit 1,a
jp z,ia_spsd
ld a,1
ld (TX_State),a
ld a,(SlotTime)
ld (TX_Timer),a
ia_spsd:
endif
pop hl
pop af
ei
reti
;---------------------------------------------------------------------------
; for ext/status interrupts on Modem, get DCD state into memory, and
; deallocate any spurious buffers (buffer stuff done 30 Sep 86).
ia_ext:
push af
ld a,10h ; reset ext/status interrupts
out (A_ctl),a
in a,(A_ctl) ; grab RR0
ld (A_RR0),a
bit 4,a ; [JS] check sync/hunt bit
jp nz,ia_ex1 ; [JS] no need to worry, if not zero
ld a,(RX_State) ; [JS] it is 0! Did it change?
or a ; [JS]
jp nz,ia_ex9 ; [JS] no, this is a DCD,CTS or EOM-interrupt
ld a,TRUE ; [JS] indeed, it changed!
ld (RX_State),a ; [JS] next time, we'll know
ld a,(RX_Allocated_Buffer) ; if we are not in the
; receiving state...
or a ; then there are no allocated buffers and...
jp z,ia_ex9 ; we MUST NOT "release" them !!! 10 Sep 86
; if no buffers allocated !!!
xor a
ld (RX_Allocated_Buffer),a ; not receiving
push hl
ld hl,(RX_head)
call free_chain ; free up all buffer(s)
pop hl
jp ia_ex9
ia_ex1:
xor a ; [JS] Prepare for next frame start
ld (RX_State),a ; [JS]
ld (RX_flushing),a
ia_ex9:
pop af
ei
reti
; include IB.MAC ;TTY interrupt catchers
;---------------------------------------------------------------------------
; we get here whenever -cts, -dcd or -sync inputs change, as well as break
; detection. Since -dcd
; is always tied to +5 volts, we need only worry about -cts and -sync.
; -cts is wired to pin 20, DTR, of the RS232 connector, and is supposed to
; be used for host to TNC handshaking; we ignore this transition (We assume
; that the host is always ready). We also ignore break detection. We are
; only interested in -sync transitions, so we can keep time.
; NOTE! This is the ONLY routine that is allowed to use the other reg set!!
; deal with break detection...
sync_hunt equ 10h
ib_ext:
ex af,af'
exx ; we want the other registers
ld a,10h
out (B_ctl),a ; reset ext/status interrupts
in a,(B_ctl) ; grab RR0
ld d,a ; Hold it for a moment...
and sync_hunt ; isolate this bit
jp z,ib_s0
;else sync/hunt is a 1
ld a,c
or a
jp z,ib_s1 ; go here if state of sync/hunt changed
; Here if sync/hunt bit did NOT change - maybe something else did....
ib_s9:
ld a,d ; retreive RRO from above
and Break_Abort ; Check if we are doing a break/abort thing
jp z,ib_NBA ; There if No break/abort
; Else Break/Abort bit on, note state change...
ld a,TRUE
ld (in_break),a ; save in mem (probably can use E reg...)
in a,(B_dat) ; clear out any null character from buffer
jp ib_BOK ; Break OK for now...
ib_NBA: ;if no break/abort, check if we are in break/abort state.
ld a,(in_break)
or a
jp z,ib_BOK ; Nothing going on, Break OK
; Else we were in break mode, and this is the tail end of a break.
xor a
ld (in_break),a
in a,(B_dat) ; discard the single extraneous null
ib_BOK:
ib_s99:
ex af,af'
exx
ei
reti ; else something else & we don't care
ib_s0: ; sync/hunt is a 0
ld a,c
or a
jp nz,ib_s1a ; go here if sync/hunt changed
jp ib_s9 ; else not interested, forget it
;get here if state of sync/hunt changed
ib_s1:
ld c,1
jp ib_s1b
ib_s1a: ; first fix up C for next tick
ld c,0
ib_s1b:
; Here when we've seen a real "clock tick" & dealt with C reg
inc b
ld a,b
cp 12
jp nz,ib_s99 ; we act on every 12th clock tick...
ld b,0 ; so reload divisor. This give us an
; effective interrupt rate of 100 Hz
; Decrement all the timers
ld a,(TX_Timer) ; Get value, and ...
or a
jp z,ib_s1c
dec a ; ... decrement it as required.
ld (TX_Timer),a
ib_s1c:
jp ib_s99
;---------------------------------------------------------------------------
ib_special:
push af
ib_sp9: ; Normal exit
ld a,30h ; error reset
out (B_ctl),a
pop af
ei
reti
;---------------------------------------------------------------------------
; The TX has become empty, shove a new character out
ib_tbe:
push af ; new char will return in A
push hl
ld a,(Out_esc_mode)
or a
jp z,ib_t1 ; not escaped, so go here
; else we are escaped, so send escaped char
ld a,(Out_char) ; char which follows escape
or a
jp z,ib_t2 ; special case if at end of frame, clean up
out (B_dat),a
xor a
ld (Out_esc_mode),a ; get out of escaped mode
jp ib_t9 ; all for now...
ib_t1:
ld hl,(out_chain_head) ; we are currently on this buffer,
call getchar ; as getchar() needs to know
ld (out_chain_head),hl ; maybe HL changed,so save it in case
jp z,ib_tdone ; if no more chars, deal with this
cp FESC
jp z,ib_t1a ; deal with FESC char in data stream
cp FEND
jp z,ib_t1b ; deal with FEND char in data stream
; else this char is nothing special, so shove it out
out (B_dat),a ; shove it out
jp ib_t9 ; if this is not last char, all for now
; else this is last char, send FEND
ib_tdone:
ld a,FEND
out (B_dat),a
ld a,TRUE
ld (Out_esc_mode),a ; set special escaped mode by...
xor a
ld (Out_char),a ;... making escaped char a 0
jp ib_t9 ; all till TX Buffer goes empty again.
; here if are completely done sending frame
ib_t2:
push de ; need this for a moment
ld hl,(out_head_cbuf)
inc hl
inc hl
ld de,out_bottom
or a
push hl
sbc hl,de
pop hl ; this may be the one we want
pop de
jp nz,ib_t2a ; yes it is!
ld hl,Out_Top ; else, make a circular buffer
ib_t2a:
ld (out_head_cbuf),hl ; we will work on this one next
xor a
ld (out_started),a ; not doing outputs anymore
ld (Out_esc_mode),a ; !! NOT IN ESCAPED MODE ANYMORE !!
ld a,28h ; NEEDED for ASYNC
out (B_ctl),a ; reset TX interrupt pending
ib_t9:
pop hl
pop af
ei
reti ; now get our butts out of here...
; here is FESC in data stream
ib_t1a:
out (B_dat),a ; Ship FESC character to port
ld a,TFESC ; ready what will be next char
ib_t1z:
ld (Out_char),a ; set char for next time
ld a,TRUE
ld (Out_esc_mode),a ; we are in escaped mode
jp ib_t9 ; all for now
; here is FEND in data stream
ib_t1b:
ld a,FESC
out (B_dat),a
ld a,TFEND
jp ib_t1z ; rest is same as FESC case
;---------------------------------------------------------------------------
; Got a char from the TTY port, deal with it.
ib_rca:
push af
in a,(B_ctl) ; Read RR0; force reg pointer to be 0
ld a,1
out (B_ctl),a ; ready to read RR1
in a,(B_ctl) ; Grab RR1
and Framing_Error ; Isolate the FE bit
jp z,ib_Rtop ; No Framing Error, so process this char
; Else we have a Framing Error - Ignore this char & flush this frame...
call STA_off ; Off with the LED!
in a,(B_dat) ; Flush erroneous character
xor a
ld (In_state),a ; Force receiver to look for FEND
ld a,(In_Allocated_Buffer)
or a
jp z,ib_rc9 ; If no buffer is allocated, done; Exit.
; Else we were receiving a data SLIP frame, so flush it.
push hl
ld hl,(In_head)
call free_chain ; Dump these buffers back to free list
pop hl
jp ib_rc9 ; And get out of here!
ib_rTop:
ld a,(In_state) ; get our state machine value
or a
jp z,ib_r0 ; in state 0, waiting for FEND
cp 1
jp z,ib_r1 ; in state 1, saw FEND
cp 2
jp z,ib_r2 ; in state 2, data to follow
cp 3
jp z,ib_r3 ; saw FESC, expecting TFESC or TFEND
cp 10
jp z,ib_r10 ; Expecting TXdelay
cp 20
jp z,ib_r20 ; Expecting P value
cp 30
jp z,ib_r30 ; Expecting SlotTime value
cp 40
jp z,ib_r40 ; Expecting TailTime value
cp 50
jp z,ib_r50 ; Expecting Full/Half duplex value
ifdef HARDWARE
cp 60
jp z,ib_r60 ;[JDZ] Expecting Set Hardware value
ifndef TASCO
cp 61
jp z,ib_r61 ;[JDZ] Expecting Set Hardware value
endif
endif
;else we don't know what happened, ignore it.
ib_rcjunk:
in a,(B_dat)
ib_rcFEND:
xor a
ib_rcSTATE:
ld (In_State),a ;go into In_State 0, FEND hunt
ib_rc9:
pop af ; throw it away, we don't need junk
ei
reti
; Here if we are hunting for FEND character
ib_r0:
call STA_off
in a,(B_dat)
cp FEND
jp nz,ib_rc9 ; if we didn't see an FEND, keep looking
; else is an FEND, change state
ld a,1
jp ib_rcSTATE
; Get here if we've seen FEND character; look for command byte
ib_r1:
call STA_off
in a,(B_dat)
cp FEND
jp z,ib_rc9 ; Just another FEND, keep looking for cmd
call STA_on ;getting valid SLIP; show in STA LED
; Here if we DO NOT have an FEND (expecting command byte)
ifndef ALONE
cp 0ffh
jp z,kiss_exit
endif
and 0fh
jp z,ib_r1a ; 0 command means data will follow
cp 1
jp z,ib_r1b ; 1 command means TXdelay will follow
cp 2
jp z,ib_r1c ; 2 command means P(Persistence) will follow
cp 3
jp z,ib_r1d ; 3 command means Slot Time will follow
cp 4
jp z,ib_r1e ; 4 command means TailTime to follow
cp 5
jp z,ib_r1f ; 5 command means Full/Half duplex to come
ifdef HARDWARE
cp 6
jp z,ib_r1g ; 6 command means Set Hardware to come
endif
; Here if we receive bogus command byte, flush rest of frame
call STA_off ;bogosity, so turn off STA LED
jp ib_rcFEND
; exit kiss mode.
ifndef ALONE
kiss_exit:
ld hl,(000eh)
res 4,(hl)
ld hl,0000h
push hl
ld hl,(0019h)
jp (hl)
endif
; Data are expected, change state
ib_r1a:
ld a,2
jp ib_rcSTATE
; TXdelay to follow, change state
ib_r1b:
ld a,10
jp ib_rcSTATE
; P to follow, change state
ib_r1c:
ld a,20
jp ib_rcSTATE
; SlotTime to follow, change state
ib_r1d:
ld a,30
jp ib_rcSTATE
; TailTime to follow, change state
ib_r1e:
ld a,40
jp ib_rcSTATE
; Full/Half Duplex to follow, change state
ib_r1f:
ld a,50
jp ib_rcSTATE
ifdef HARDWARE
; Set Hardware to follow, change state
ib_r1g:
ld a,60
jp ib_rcSTATE
endif
; These bytes are data
ib_r2:
in a,(B_dat)
cp FEND
jp z,ib_r2b ; FEND means to queue this buffer
push af ; Save the char we read on stack for a bit..
ld a,(In_Allocated_Buffer)
or a
jp nz,ib_r2c ; if we already allocated buffer
push hl
call allocate_buffer ; get our initial buffer to mess with
jp nz,ib_r22
; else no room, flush this frame
pop af
pop hl ; keep stack tidy
jp ib_rcFEND
ib_r22:
ld a,TRUE
ld (In_Allocated_Buffer),a ; make ourselves active
ld (In_buffer),hl
ld (In_head),hl ; save current & head of chain pointers
pop hl
ib_r2c:
pop af ; Retreive the data char we just got...
cp FESC
jp z,ib_r2a ; If FESC in data stream, switch state
push hl
ld hl,(In_buffer)
call putchar ; shove this character into our buffer
jp nc,ib_r2ca
xor a
ld (In_Allocated_buffer),a
ld hl,(In_head)
call free_buffer
pop hl
jp ib_rcFEND
ib_r2ca:
ld (In_buffer),hl ; save in case HL changed
pop hl
jp ib_rc9 ; done so far
; FESC character seen while grabbing data
ib_r2a:
ld a,3
jp ib_rcSTATE
; FEND character seen while grabbing data
ib_r2b:
ld a,(In_Allocated_Buffer)
or a
jp z,ib_r2z ; No bytes accumulated, so is null frame
; else we must ship this frame to TX
push hl ; This bug found 29 Sep (must save HL !!!)
ld hl,(In_Buffer)
call putchar ; put a garbage character at the end of
; last buffer because getchar() will strip
; it. Hack needed because of RX use of
; putchar/getchar.
ld hl,(In_head)
jp nc,ib_r2za
call free_chain
jp ib_r2zb
ib_r2za:
call TX_queue_insert
ib_r2zb:
pop hl
xor a
ld (In_Allocated_Buffer),a ; input no longer active
ib_r2z: ; entry point for null frame
call STA_off ;done getting this frame, turn STA LED off
ld a,1 ; Keep as was, FENDs only at end in v.32
jp ib_rcSTATE
; here if we've seen FESC in data stream
ib_r3:
in a,(B_dat)
cp TFESC
jp z,ib_r3a
cp TFEND
jp z,ib_r3b
; Else we don't know what the hell it is, so ignore & keep collecting bytes
ld a,2
jp ib_rcSTATE
; here if we've seen TFESC after an FESC in data stream; write an FESC
ib_r3a:
ld a,FESC
ib_r3z:
push hl
ld hl,(In_buffer)
call putchar
jp nc,ib_r3za
xor a
ld (In_Allocated_buffer),a
ld hl,(In_head)
call free_buffer
pop hl
jp ib_rcFEND
ib_r3za:
ld (In_buffer),hl
pop hl
ld a,2
jp ib_rcSTATE
; Here if we've seen TFEND after FESC in data stream; write FEND
ib_r3b:
ld a,FEND
jp ib_r3z ; rest is same as for TFESC case
; This character is interpreted as TXdelay
ib_r10:
in a,(B_dat)
ld (TXdelay),a
jp ib_rcFEND
; This charcter is P, Persistence value
ib_r20:
in a,(B_dat)
ld (Persistence),a
jp ib_rcFEND
; This character is SlotTime value
ib_r30:
in a,(B_dat)
ld (SlotTime),a
jp ib_rcFEND
; This character is TailTime value
ib_r40:
in a,(B_dat)
ld (TailTime),a
jp ib_rcFEND
; This character is Full/Half Duplex value
; 0 means Half Duplex, non-zero means Full Duplex
ib_r50:
in a,(B_dat)
ld (Full_Duplex),a
jp ib_rcFEND
ifdef HARDWARE
; This character is Set Hardware value
; data means Output Address.
ib_r60:
in a,(B_dat)
cp 0feh ; CTS flow off
jp nc,ib_r60cts
cp 0fch ; Reserved
jp nc,ib_rcFEND
cp 0f8h ; DCD
jp nc,ib_r60DCD
ifndef TASCO
cp 020h
jp nc,ib_rcFEND
add a,0a0h ; A5=1 OUT_DATA address, Ch A
ld (Out_Address),a
ld a,61
jp ib_rcSTATE
else
jp ib_rcFEND
endif
; CTS flow
ib_r60cts:
sub 0feh
ld (CTS_Control),a
jp ib_rcFEND
; Software DCD
ib_r60dcd
sub 0f8h
ld (Soft_DCD),a
jp ib_rcFEND
ifndef TASCO
; data means Output Data.
ib_r61:
push bc
ld a,(Out_Address)
ld c,a
in a,(B_dat)
out (c),a
pop bc
jp ib_rcFEND
endif
endif
; include BUFFERS.MAC ;all buffer-related stuff in here
;plus all (eventually) variables
;
; The buffer list is kept from "bottom" to the end of RAM. The format of the
; buffers is:
;+------+--------+-------+---------------------------------------------------+
;| next | Nbytes | Nread | data |
;+------+--------+-------+---------------------------------------------------+
;
; 2 bytes 1 byte 1 byte 124 bytes (Total 128 bytes)
; next Pointer to next buffer on this buffer chain (or 0 if no more)
; Nbytes Number of bytes in this buffer that are valid
; Nread Number of bytes read from this buffer (used by getchar)
; data 124 bytes of data (not all is necessarily valid, see Nbytes field)
;
; The buffer pool is all here, and as processes need buffer space, it is all
; allocated out of this pool. See allocate_buffer and free_buffer code.
;---------------------------------------------------------------------------
; return in HL a pointer to a free buffer. If there are not more buffers,
; return with Z flag set.
; destroys no registers except return value HL.
; IS CALLED FROM AN INTERRUPT ROUTINE, so this operation is atomic.
allocate_buffer:
push bc
push af
ld hl,(free) ;get pointer to head of free list
ld a,h
or l
jp nz,OK_allocate_buffer ; assure we're not off the end
;get here if no more buffers. Return Z set - do not disturb A.
pop af
ld b,a ; tuck A away for a moment...
xor a ; turn on Z bit
ld a,b ; retreive original A
pop bc
ret
OK_allocate_buffer:
xor a
ld c,(hl) ;grab lo byte of next free buffer
ld (hl),a ; clear it out
inc hl
ld b,(hl) ; "ld bc,(hl)" now hi byte
ld (hl),a ; clear it out, too
ld (free),bc ; update with new free list pointer
dec hl ; Now HL is at head of new buffer
pop af
ld b,a ; tuck A away for a moment...
ld a,1
or a ; Turn Z bit off (i.e., all OK)
ld a,b ; retreive original A
pop bc
ret
;---------------------------------------------------------------------------
; free_buffer gets passed a pointer (in HL) to a buffer to be freed. The
; buffer is placed on the head of the free list. The nbytes & nread fields
; are made 0 before placing on free list.
; THIS ROUTINE IS CALLED AT INTERRUPT LEVEL, so results are atomic.
; no registers are disturbed at all. The FREE pointer is updated, however.
; 159 T states [ 63.6 usec @ 2.5 MHz ]
free_buffer:
push af
push bc ;we'll use these
push hl ;this will be new head of free list
ld bc,(free) ;get old free head
ld (hl),c ;put on free chain, first low byte...
inc hl
ld (hl),b ; ...now hi byte
xor a
inc hl
ld (hl),a ; zero out nbytes field
inc hl
ld (hl),a ; and the nread field of new head of free
pop hl ;get new head of free list back
ld (free),hl ;and save it in memory where it belongs
pop bc
pop af
ret
; --------------------------------------------------------------------------
; putchar - HL contains pointer to buffer, A contains the character to put
; into the buffer. Upon return, char is put into this buffer if ther is
; room, else another buffer is allocated and HL is updated to point to this
; new buffer. The new buffer is chained onto the old buffer in this case.
; The calling routine is responsible for maintaing both the head of a
; particular buffer chain (if it needs it), and the current buffer being
; manipulated. THIS ROUTINE IS CALLED AT INTERRUPT LEVEL, so is atomic. No
; registers disturbed, except that HL may have a new value.
; 211 T states [ 84.4 usec @ 2.5 MHz ] no new buffer required
; 338 T states [ 135.2 usec @ 2.5 MHz ] New buffer needed
; [JDZ] If carry flag is on, then memory is not enough to putchar.
putchar:
push bc
push ix
push af
push hl ;do it this way for a reason...
pop ix ;get buffer pointer into IX
ld a,(ix+2) ;grab nbytes field
cp 124 ;max of 124 chars in a buffer
jp nz,putc1
call putc_need_new_buffer
; if it takes this call, it returns with a new buffer, with HL pointing to
; it (as well as IX), and with A reg set to 0.
; else just plunk into buffer
jp nc,putc1
pop af
scf
jp putc2
putc1:
inc (ix+2) ;one more char will go into this buffer
ld c,a ;get previous nbytes
xor a
ld b,a ; bc <- nbytes, filled out to 16 bits
add ix,bc ; update ix to point to where char goes
pop af ; retreive the char we want to save
ld (ix+4),a ; save it in this buffer
or a ; reset Cy flag.
putc2:
pop ix
pop bc
ret ;done for the moment
; 127 T states [ 50.8 usec @ 2.5 MHz ] (really part of prev routine)
putc_need_new_buffer: ;prev buffer filled, get a new one
push de ; working registers
push hl ; save current buffer pointer
call allocate_buffer ; grab a new buffer, addr is in HL
jp nz,putcnb1
pop hl
scf
jp putcnb2
putcnb1:
ex de,hl ; "ld de,hl" - get new addr into DE for now
pop hl
ld (hl),e ; link new buffer onto chain, lo byte first
inc hl
ld (hl),d ; now hi byte, chaining done
ex de,hl ; update HL for orig. calling routine's use
push hl
pop ix ; upper routine needs ix pointing to new buf
xor a ; and A is nbytes in calling routine, make..
; zero for a new buffer
putcnb2:
pop de ; done with this working register
ret ; all done here, let calling routine finish
; --------------------------------------------------------------------------
; getchar - grab a character from the buffer pointed at by HL, return in A.
; if the "nread" field of this buf = "nbytes" then this buffer is exhausted,
; so follow the chain on to the next buffer & release old buffer. If the
; next chain is 0, or if the nbytes field is >= nread field, then there are
; no more bytes. In this case, return with Z bit set; normally return with
; Z bit reset (That is, non-zero) indicating a valid char is in A. Note
; that if we need to follow the chain to a new buffer, HL will be updated,
; too, so that the calling routine needs to deal with this.
; no registers changed except AF and possibly HL.
; CALLED AT INTERRUPT LEVEL, so operation is atomic.
; 212 T states [ 84.8 usec @ 2.5 MHz ] No new buffer needed
; 493 T states [ 197.2 usec @ 2.5 MHz ] if following chain
getchar:
push ix ; save because is working reg
push bc ; working regs here
push hl
pop ix ; ix points to this buffer
ld a,(ix+3) ; grab Nread
cp (ix+2) ; compare with Nbytes
call z,getc_new_buf ; if they are same, this buffer is spent
inc (ix+3) ; we are reading one more char, update Nread
inc a
cp (ix+2)
jp nz,getc_pluck_character ; if not looking at last character
; else, is the "next" pointer 0?
push hl
ld b,a ; !!!!! SAVE A REG !!!!!!! 4 Jan 87
ld a,(hl)
inc hl
or (hl)
ld a,b ; !!!! Restore A Reg (Gasp!)
pop hl
jp nz,getc_pluck_character
; else next is 0 and we are on last char - flush it & quit
call free_buffer
pop bc
pop ix
ret ; note that Z bit is set (from above)
; else we can just pluck a character out of this buffer
getc_pluck_character:
dec a ; fix A from above mucking around...
ld c,a ; get old Nread into BC
ld b,0 ; ditto
add ix,bc ; fix buffer pointer
ld a,1
or a ; make Z bit reset
ld a,(ix+4) ; get the desired byte
pop bc
pop ix
ret ; all for this simple case
; old buffer is spent, get new one (if any)
getc_new_buf:
push de ; need this reg here
ld e,(hl) ; get lo byte of Next pointer
inc hl
ld d,(hl) ; hi byte of Next pointer (now all in DE)
dec hl ; HL now back to point at spent buffer
call free_buffer ; give the buffer back
ex de,hl ; "ld hl,de" - follow chain
push hl
pop ix ; init new IX (same as HL in this routine)
xor a ; A holds Nread (needed above)
pop de ; release DE from use by this excursion
ret
; --------------------------------------------------------------------------
; free_chain - MUST be called from interrupt routine to guarantee
; atomicity. Takes buffer chain pointed at by HL and returns them to free
; buffer list
; 303 T states + (n_on_chain-1)*238 T states
; [ 121.2 usec + (n_on_chain-1)*95.2 usec ]
free_chain:
push af
push de
push hl ; we will muck with these
fc_0:
ld e,(hl) ; get lo part of next buffer pointer
inc hl
ld d,(hl) ; now hi part of next buffer pointer
dec hl
call free_buffer ; release this buffer
ld a,d
or e
jp z,fc_9 ; if "next" address is 0, we are at end
; else we've got more on this chain - deal with them.
ex de,hl ; "ld hl,de" - HL points to "next"
jp fc_0
fc_9:
pop hl
pop de
pop af
ret
; --------------------------------------------------------------------------
; out_queue_insert - Places the just-received buffer on the output queue.
; The address of the RX buffer just received is in HL.
; The output queue is a circular buffer. The output routine keeps sending
; out buffers until its out_head_cbuf pointer equals its out_tail_cbuf
; pointer. The output routine never mucks with the out_tail_cbuf pointer;
; similarly, this routine never changes the out_head_cbuf pointer. So it
; is possible to
; insert new entries into the output circular buffer queue without
; disturbing the entry which is being sent to the output port.
out_queue_insert:
push af
push de
push hl ; use these
ex de,hl ; "ld de,hl" - move buffer to link addr
ld hl,(out_tail_cbuf) ; Grab next free location
ld (hl),e ; set lo addr 1st
inc hl
ld (hl),d ; now hi addr
inc hl ; Now HL points to next free entry in...
ld de,out_bottom ; ...circ buf, unless we're at end
or a ; clear carry
push hl ; (may be be needed address)
sbc hl,de
pop hl ; get back what we think is good
jp nz,oqi_0
ld hl,Out_Top ; get here if we're at end of circ buffer.
oqi_0:
ld (out_tail_cbuf),hl
pop hl
pop de
pop af ; keep clean
ret
;---------------------------------------------------------------------------
; TX_Queue_Insert - similar to Out_queue_insert, but with different queue.
; Also, increments the byte TX_Outstanding (which counts the number of
; frames ready to be dumped to the modem port). This routine, like
; out_queue_insert, does not need to worry about queue wrap-around because
; there are more entries in each of these queues than there are buffers
; available. Yes, I know this is a hack, and wastes some RAM space, but it
; means I don't have to check for overflows here.
; The queue is circular, and sometimes I call it a "CBuf" - Circular Buffer
TX_Queue_Insert:
push af
push de
push hl
ex de,hl ; "ld de,hl" - save chain head in DE
ld hl,(TX_Tail_CBuf) ; Next free location in TX CBuf
ld (hl),e
inc hl
ld (hl),d ; put this chain into TX Queue
inc hl ; HL is next availble TX Queue ...
ld de,TX_Bottom ; ... unless we are at bottom of ...
or a ; ... the TX Queue
push hl
sbc hl,de
pop hl
jp nz,TQI_0 ; go there if not at buffer bottom
ld hl,TX_Top ; else reload with top of queue val
TQI_0:
ld (TX_Tail_CBuf),hl ; save next free queue slot
ld hl,TX_Outstanding
inc (hl) ; +1 more frame outstanding now
pop hl
pop de
pop af
ret
;-----------------------------------------------------------------------------
; Setup HL & TX_Chain_Head for transmission of next chain.
TXnext_CBuf:
push af
push de
ld hl,(TX_Head_CBuf)
ld e,(hl)
inc hl
ld d,(hl) ; DE -> next chain to transmit
inc hl ; HL MIGHT be next CBuf entry pointer
push de
ld de,TX_Bottom
or a ;clear carry
push hl ;save what might be correct value
sbc hl,de
pop hl
pop de
jp nz,TXn_1 ;go there if not at end of circ. buf
ld hl,TX_Top ;else we wrap aroune
TXn_1:
ld (TX_Head_CBuf),hl ;save next circ buf pointer in mem
ex de,hl ;return ptr to next chain to TX in HL
ld (TX_Chain_Head),hl ;TX RCA routine needs this
pop de
pop af
ret
;-----------------------------------------------------------------------------
STA_on: ;Turn the STA LED on. ASSUMES that interrupts are disabled!
push af
ld a,5
out (A_ctl),a ; ready to write WR5
ld a,(A_WR5) ; get memory copy
and NOT ALED ; set DTR bit to 0 so LED goes on
out (A_ctl),a ; Actually turn on STA LED now...
ld (A_WR5),a ; update memory copy
pop af
ret
;-----------------------------------------------------------------------------
STA_off: ;Turn the STA LED off. ASSUMES that interrupts are disabled!
push af
ld a,5
out (A_ctl),a ; ready to write WR5
ld a,(A_WR5) ; get memory copy
or ALED ; set DTR bit to 1 so LED goes off
out (A_ctl),a ; Actually turn off STA LED now...
ld (A_WR5),a ; update memory copy
pop af
ret
;These routines MUST be called with interrupts disabled!
;-----------------------------------------------------------------------------
STA_flip:
push af
in a,(A_ctl) ;assure we are talking to ch 0
ld a,5
out (A_ctl),a ; ready to write WR5
ld a,(A_WR5) ; get memory copy
xor ALED ; [JDZ]
out (A_ctl),a ; Actually turn off STA LED now...
ld (A_WR5),a ; update memory copy
pop af
ret
;-----------------------------------------------------------------------------
CON_on:
push af
ld a,5
out (B_ctl),a
ld a,BLEDon
ld (B_WR5),a ; save in mem for flip routine
out (B_ctl),a
pop af
ret
;-----------------------------------------------------------------------------
CON_off:
push af
ld a,5
out (B_ctl),a
ld a,BLEDoff
ld (B_WR5),a ; save in mem for flip routine
out (B_ctl),a
pop af
ret
;-----------------------------------------------------------------------------
CON_flip:
push af
in a,(B_ctl) ;assure we are talking to ch 0
ld a,5
out (B_ctl),a ; ready to write WR5
ld a,(B_WR5) ; get memory copy
xor BLED ; [JDZ]
out (B_ctl),a ; Actually turn off CON LED now...
ld (B_WR5),a ; update memory copy
pop af
ret
; SIO Initialize data.
a_init:
db 18h,4,20h,1,1bh,7,7eh,5,ALEDoff,3,0c9h ;For Modem
a_size equ $-a_init
b_init: ;[JDZ] For TTY
db 18h,4,44h,2,I_Vector mod 256,3,Auto_Enable,5,BLEDoff,1,1fh
b_size equ $-b_init
;This is the data area which gets blasted into RAM upon startup: [JDZ]
data_init:
TXdelay equ Free_RAM - data_init + $
db 33 ; TX delay default is 330 ms
Persistence equ Free_RAM - data_init + $
db 63 ; default value for Persistence
SlotTime equ Free_RAM - data_init + $
db 5 ; and Slot Time defaults to 50 ms
TailTime equ Free_RAM - data_init + $
db 3 ; (should be 11 for 300 baud)
; Tail Timer default
Full_Duplex equ Free_RAM - data_init + $
db 0 ;not Full Duplex to start
CTS_Control equ Free_RAM - data_init + $
db 0 ;not CTS Control to start
Soft_DCD equ Free_RAM - data_init + $
db 1 ;not Soft DCD to start
RX_State equ Free_RAM - data_init + $
db 0 ;Means we are in Recieving State
RX_Allocated_Buffer equ Free_RAM - data_init + $
db 0 ;set non-zero if we're in RX state
RX_buf equ Free_RAM - data_init + $
dw 0 ;address of current Receive buffer
RX_head equ Free_RAM - data_init + $
dw 0 ;address of 1st RX buffer
RX_Flushing equ Free_RAM - data_init + $
db 0 ;is non-0 if we ran out of buffer
;space and are currently flushing this
;frame being received. Used by
;ia_rca and reset by ia_ext.
TX_State equ Free_RAM - data_init + $
db 0 ;TX state.
;0 : we're not in TX state
;1 : we're in slot time state
;2 : we're in TX delay state
;3 : we're in waiting CTS is turn on
;4 : we're in Transmit data state
;5 : we're in Tail time state
TX_Started equ Free_RAM - data_init + $
db 0 ;non-zero if we've begun TXing chars
TX_Outstanding equ Free_RAM - data_init + $
db 0 ;Number of TX CBufs queued up for TX
TX_Head_CBuf equ Free_RAM - data_init + $
dw TX_Top ;Current active CBuf entry (if active)
TX_Tail_CBuf equ Free_RAM - data_init + $
dw TX_Top ;next free CBuf entry
TX_Chain_Head equ Free_RAM - data_init + $
dw 0 ;holds address of the current buffer
;chain head that we are transmitting
TX_Timer equ Free_RAM - data_init + $
db 0
A_RR0 equ Free_RAM - data_init + $
db CTS
A_WR5 equ Free_RAM - data_init + $
db ALEDoff ;state of STA LED & RTS (PTT) line,
;mainly... (For Ch A only [modem] )
B_WR5 equ Free_RAM - data_init + $
db BLEDoff
;these next two are used by the IB_TBE interrupt routine.
Out_esc_mode equ Free_RAM - data_init + $
db 0 ; not in escaped mode
Out_char equ Free_RAM - data_init + $
ds 1 ; next char to send if escaped mode
in_break equ Free_RAM - data_init + $
db 0 ; non-zero if we are in a break detect
; sequence on the async port
In_Buffer equ Free_RAM - data_init + $
dw 0 ;addr of current Input buffer
In_Head equ Free_RAM - data_init + $
dw 0 ;addr of 1st Input Buffer
In_Allocated_Buffer equ Free_RAM - data_init + $
db 0 ;is not 0 if we've already alloc'd buf
In_State equ Free_RAM - data_init + $
db 1 ;convert back to 1 in v.32 code
;input state machine state
;4 Mar 8: Make it 0 (from 1) becuz
;noise on line is first triggering the
;code to assume that a frame is coming
;from the host..... Comment below was
;appropriate before
;assume that we've seen an FEND from
;(non-existent) "previous" frame. This
;means that when we are receiving data
;from user, there need be ONLY the
;FEND char at the end of a frame, and
;not at the beginning (although if a
;FEND is at the beginning, it is
;ignored.)
Out_Started equ Free_RAM - data_init + $
db 0 ;Output not started yet (Logical var)
Out_Head_CBuf equ Free_RAM - data_init + $
dw Out_Top ;address of buffer to be output rs232
Out_Tail_CBuf equ Free_RAM - data_init + $
dw Out_Top ;pointer to next free output buffer
Out_Chain_Head equ Free_RAM - data_init + $
dw 0 ;addr of buffer we are now outputting
free equ Free_RAM - data_init + $
dw Bottom ;address of 1st buffer on free list
data_size equ $-data_init
Out_Address equ free+2 ;address of output following data
;ds 1
buffer_area equ Out_Address+1
TX_Top equ buffer_area ;"top" of output circular buffer
; 255 out buffer chains pending, max
TX_Bottom equ TX_Top+2*255 ;"bottom" of output circular buffer
Out_Top equ TX_Bottom+2
Out_Bottom equ Out_Top+2*255
Bottom equ Out_Bottom+2 ;end of all code & predefined data
;***************************************************************************
; out = to TTY port; in = from TTY port
; TX = to modem; RX = from modem
;
; ; means that that code executes without interrupts enabled (except
; for the initialization code)
end start